package spatialanalyst;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import org.osoa.sca.annotations.Service;



import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.display.CancelTracker;
import com.esri.arcgis.geodatabase.DENetworkDataset;
import com.esri.arcgis.geodatabase.FeatureCursor;
import com.esri.arcgis.geodatabase.GPMessages;
import com.esri.arcgis.geodatabase.IDataset;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.NetworkDataset;
import com.esri.arcgis.geodatabase.NetworkDatasetWorkspaceExtension;
import com.esri.arcgis.geodatabase.QueryFilter;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriDatasetType;
import com.esri.arcgis.geoprocessing.GeoProcessor;
import com.esri.arcgis.geoprocessing.tools.datamanagementtools.SaveToLayerFile;
import com.esri.arcgis.networkanalyst.INAClass;
import com.esri.arcgis.networkanalyst.INAClassFieldMap;
import com.esri.arcgis.networkanalyst.INAClassLoader;
import com.esri.arcgis.networkanalyst.INAContext;
import com.esri.arcgis.networkanalyst.INALayer;
import com.esri.arcgis.networkanalyst.INASolver;
import com.esri.arcgis.networkanalyst.NAClassFieldMap;
import com.esri.arcgis.networkanalyst.NAClassLoader;
import com.esri.arcgis.networkanalyst.NAContext;
import com.esri.arcgis.networkanalyst.NALayer;
import com.esri.arcgis.networkanalyst.NARouteSolver;
import com.esri.arcgis.networkanalyst.esriNAOutputLineType;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.IStringArray;
import com.esri.arcgis.system.ITrackCancel;
import com.esri.arcgis.system.UID;
import com.esri.arcgis.system.esriLicenseExtensionCode;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;
@Service(RouteService.class)
public class RouteServiceImpl implements RouteService {

	public void SolveRoute(String shapeWorkspacePath, String inputStopsFC,
			String shapeInputNameField, String networkDatasetName,
			String outLayerPath) {
		// TODO Auto-generated method stub

		// Open the feature workspace, input feature class, and network dataset
		try {
			ShapefileWorkspaceFactory workspaceFactory = new ShapefileWorkspaceFactory();

			Workspace workspace = (Workspace) workspaceFactory.openFromFile(
					shapeWorkspacePath, 0);
			IFeatureClass inputStopsFClass = workspace
					.openFeatureClass(inputStopsFC);

			UID uid = new UID();
			uid.setValue("esriGeoDatabase.NetworkDatasetWorkspaceExtension");
			NetworkDatasetWorkspaceExtension networkDatasetWorkspaceExtension = new NetworkDatasetWorkspaceExtension(
					workspace.findExtension(uid));
			IDataset dataset = networkDatasetWorkspaceExtension
					.getDatasetByName(esriDatasetType.esriDTNetworkDataset,
							networkDatasetName);
			NetworkDataset networkDataset = new NetworkDataset(dataset);

			// Create the Route NALayer
			INALayer naLayer = createRouteAnalysisLayer("Route", networkDataset);
			INAContext naContext = naLayer.getContext();
			INAClass stopsNAClass = (INAClass) naContext.getNAClasses()
					.getItemByName("Stops");

			// Load the Stops
			INAClassFieldMap naClassFieldMap = new NAClassFieldMap();
			naClassFieldMap.setMappedField("Name", shapeInputNameField);

			INAClassLoader naLoader = new NAClassLoader();
			naLoader.setLocatorByRef(naContext.getLocator());
			naLoader.setNAClassByRef(stopsNAClass);
			naLoader.setFieldMapByRef(naClassFieldMap);

			int[] rowsInCursor = { 0 };
			int[] rowsLocated = { 0 };
			ITrackCancel cancelTracker = new CancelTracker();
			FeatureCursor cursor = new FeatureCursor(inputStopsFClass.search(
					new QueryFilter(), false));
			naLoader.load(cursor, cancelTracker, rowsInCursor, rowsLocated);

			// Solve
			INASolver naSolver = naContext.getSolver();
			naSolver.solve(naContext, new GPMessages(), cancelTracker);

			// Save the layer to disk
			SaveLayerToDisk(naLayer, outLayerPath);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private INALayer createRouteAnalysisLayer(String layerName,
			NetworkDataset networkDataset) {
		NALayer naLayer = null;
		try {
			NARouteSolver naRouteSolver = new NARouteSolver();

			// Get the NetworkDataset's Data Element
			DENetworkDataset deNetworkDataset = (DENetworkDataset) networkDataset
					.getDataElement();

			// Create the NAContext and bind to it
			NAContext naContext = (NAContext) naRouteSolver.createContext(
					deNetworkDataset, layerName);
			naContext.bind(networkDataset, new GPMessages());

			// Create the NALayer
			naLayer = (NALayer) naRouteSolver.createLayer(naContext);
			naLayer.setName(layerName);

			// Set some properties on the the route solver interface
			naRouteSolver.setFindBestSequence(true);
			naRouteSolver.setPreserveFirstStop(true);
			naRouteSolver.setPreserveLastStop(false);
			naRouteSolver.setUseTimeWindows(false);
			naRouteSolver
					.setOutputLines(esriNAOutputLineType.esriNAOutputLineTrueShapeWithMeasure);

			// Set some properties on the general INASolverSettings interface
			IStringArray restrictions = naRouteSolver
					.getRestrictionAttributeNames();
			restrictions.add("Oneway");
			naRouteSolver.setRestrictionAttributeNamesByRef(restrictions);

			// Update the context based on the changes made to the solver
			// settings
			naRouteSolver.updateContext(naContext, deNetworkDataset,
					new GPMessages());
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Return the layer...
		return naLayer;
	}

	private void SaveLayerToDisk(INALayer inLayer, String outLayer) {
		// Here, use GeoProcessing to save the layer to disk...
		try {
			GeoProcessor gp = new GeoProcessor();
			gp.setOverwriteOutput(true);

			// Never use the default constructor for a GP tool....
			SaveToLayerFile sTool = new SaveToLayerFile(inLayer, outLayer);

			// Execute the tool...
			gp.execute(sTool, null);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void initializeArcGISLicenses(AoInitialize initializer) {
		try {
			if (initializer
					.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngine) == esriLicenseStatus.esriLicenseAvailable) {
				initializer
						.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
			}
			initializer
					.checkOutExtension(esriLicenseExtensionCode.esriLicenseExtensionCodeNetwork);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public String runRoute() {
		System.out
				.println("Starting Route Layer - An ArcObjects Java SDK Developer Sample");
		String arcGISHome = null;
		try {
			arcGISHome = System.getenv("ARCGISHOME");
		} catch (Error e) {
			BufferedReader console = new BufferedReader(new InputStreamReader(
					System.in));
			System.out
					.print("Please enter the path to your ArcGIS installation directory: ");
			try {
				arcGISHome = console.readLine();
			} catch (IOException e1) {
				e1.printStackTrace();
			} // This should never happen
		}
		if (!(new File(arcGISHome).exists())) {
			System.out.println(arcGISHome + " does not exist.\nExiting...");
			System.exit(-1);
		}

		String shapeWorkspacePath = arcGISHome
				+ "/java/samples/data/networkanalyst";
		String inputStopsFC = "MultiRouteStopsWithTW";
		String shapeInputNameField = "Name";
		String networkDatasetName = "streets_nd";

		String outDataPath = arcGISHome
				+ "/java/samples/output_data/routelayer";
		String outLayerPath = outDataPath + "/routelayer.lyr";

		File outData = new File(outDataPath);
		outData.mkdirs();

		EngineInitializer.initializeEngine();
		try {
			AoInitialize aoInitializer = new AoInitialize();
			aoInitializer
					.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
			initializeArcGISLicenses(aoInitializer);

			SolveRoute(shapeWorkspacePath, inputStopsFC, shapeInputNameField,
					networkDatasetName, outLayerPath);

			System.out.println("Done.\nGenerated layer in "
					+ outData.getAbsolutePath());
			System.out.println("Add it to a map to see the results.");
			aoInitializer.shutdown();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			System.exit(-1);
		}
		return "Succeffult route";
	}
}
